#include "poll_poller.h"
#include <assert.h>
#include <chrono>
#include <cstdio>
#include <errno.h>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <vector>
#include "channel.h"
#include "trace.h"
#include "wakeuper.h"

poll_poller::poll_poller()
{
    wakeuper_ = std::make_shared<wakeuper>(this);
    wakeuper_->init();
    add_channel(wakeuper_);
    wakeuper_->set_read_event();
}

poll_poller::~poll_poller()
{
}

void poll_poller::start()
{
    while (true) {
        loop();
    }
}

void poll_poller::run_in_loop(std::function<void()> func)
{
    wakeuper_->run_in_loop(func);
}

void poll_poller::add_channel(std::shared_ptr<channel> ch)
{
    struct pollfd pfd;
    pfd.fd      = ch->fd();
    pfd.events  = static_cast<short>(ch->events());
    pfd.revents = 0;
    pollfds_.push_back(pfd);
    int idx = static_cast<int>(pollfds_.size()) - 1;
    ch->set_index(idx);
    channels_[pfd.fd] = ch;
}
void poll_poller::remove_channel(std::shared_ptr<channel> ch)
{
    auto fd = ch->fd();
    if ((channels_.find(fd) == channels_.end())) {
        std::ostringstream oss;
        oss << "remove channel of fd = " << fd << ", but channel not exist";
        printf("%s\n", oss.str().c_str());
        return;
    }
    assert(channels_[fd] == ch);
    int                  idx = ch->index();
    const struct pollfd& pfd = pollfds_[static_cast<std::size_t>(idx)];
    if ((!(pfd.fd == -fd - 1 && pfd.events == ch->events()))) {
        throw std::runtime_error("fd error");
    }
    size_t n = channels_.erase(fd);
    assert(n == 1);
    (void)n;
    if (size_t(idx) == pollfds_.size() - 1) {
        pollfds_.pop_back();
    }
    else {
        int channelAtEnd = pollfds_.back().fd;
        iter_swap(pollfds_.begin() + idx,
                  pollfds_.end() - 1);  // 交换最后一个元素 和要删除的元素
        if (channelAtEnd < 0) {
            channelAtEnd = -channelAtEnd - 1;  // 不关注的事件 被设置为负值了 原始的fd 还原一下
        }
        channels_[channelAtEnd]->set_index(idx);  // 更新索引 最后一个元素位置挪到 要删除的元素位置了
        pollfds_.pop_back();
    }
}
void poll_poller::update_channel(std::shared_ptr<channel> ch)
{
    auto fd = ch->fd();
    if ((channels_.find(fd) == channels_.end())) {
        fprintf(stderr, "channel may be remove because of disconnect\n");
        return;
    }
    int            idx = ch->index();
    struct pollfd& pfd = pollfds_[static_cast<std::size_t>(idx)];
    if ((!(pfd.fd == fd || pfd.fd == -fd - 1))) {
        throw std::runtime_error("fd error");
    }
    pfd.fd      = fd;
    pfd.events  = static_cast<short>(ch->events());
    pfd.revents = 0;
    if (ch->is_no_event()) {
        // ignore this pollfd
        pfd.fd = -fd - 1;
    }
}
void poll_poller::loop()
{
    int numEvents = ::poll(&*pollfds_.begin(), static_cast<nfds_t>(pollfds_.size()), 1);  // 1ms
    int tmp_error = errno;
    if (numEvents > 0) {
        std::vector<struct pollfd> valid_pfds;
        for (auto pfd = pollfds_.begin(); pfd != pollfds_.end() && numEvents > 0; ++pfd) {
            if (pfd->revents > 0) {
                --numEvents;
                valid_pfds.push_back(*pfd);
            }
        }
        for (auto& pfd : valid_pfds) {
            auto iter = channels_.find(pfd.fd);
            if (iter == channels_.end()) {
                continue;  // handle exception
            }
            auto      channel = iter->second;
            const int events  = pfd.revents;
#if defined(__CYGWIN__)
            if (events & (POLLIN | POLLPRI)) {
                channel->handle_read();
            }
#else
            // printf("fd=%d revents:%d\n", pfd->fd, events);
            if (events & (POLLIN | POLLPRI | POLLRDHUP)) {
                channel->handle_read();
            }
#endif
            if ((events & POLLHUP) && !(events & POLLIN)) {
                channel->handle_close();
            }
            if (events & POLLNVAL) {}
            if (events & (POLLERR | POLLNVAL)) {
                channel->handle_error();
            }
            if (events & POLLOUT) {
                channel->handle_write();
            }
        }
    }
    else if (numEvents == 0) {  // timeout
        handle_timer_event();
    }
    else {
        if (tmp_error != EINTR) {
            TRACE_SYSTEM("PollPoller::poll()");
        }
    }
}

void poll_poller::add_timer_event(const timer_event& event)
{
    timer_events_[event.id()] = event;
}

void poll_poller::handle_timer_event()
{
    const auto time_now =
        std::chrono::nanoseconds(std::chrono::system_clock::now().time_since_epoch()).count() / 1000000;

    std::vector<int> del_timers;
    for (auto ele : timer_events_) {
        if (time_now >= ele.second.trigger_time) {
            ele.second.func();
            if (ele.second.interval > 0) {
                ele.second.trigger_time += ele.second.interval;
            }
            else {
                del_timers.push_back(ele.first);
            }
        }
    }

    for (auto id : del_timers) {
        timer_events_.erase(id);
    }
}

int timer_event::id_counter = 0;